//
//  vdNode.m
//  Void Dead
//
//  Created by Sidney Just on 17.11.09.
//
//  Copyright © 2009 by Sidney Just
//  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
//  documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
//  the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
//  and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
//  PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
//  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
//  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#import "vdNode.h"


@implementation vdNode
@synthesize layer, pos_x, pos_y, aPos_x, aPos_y, rotation, scale_x, scale_y, mother, father, size_x, size_y, flags, color;
@synthesize parent, children, count, texture, receiver, currentAction;
@synthesize nodeShader, sourceBlendF, destBlendF, renderTarget, modelMatrix;

#pragma mark -
#pragma mark Misc

- (void)setMother:(vdView *)view {
	mother = view;
	
	for(int i=0; i<count; i++)
	{
		children[i].mother = mother;
	}
}

- (void)setFather:(vdKernel *)kernel {
	father = kernel;
	
	for(int i=0; i<count; i++)
	{
		children[i].father = father;
	}
}

- (vdNode *)nextNode:(vdNode *)node {
	if(count > 0)
	{
		if(!node)
			return children[0];
		
		for(int i=0; i<count; i++)
		{
			if(children[i] == node)
			{
				if(i < count-1)
				{
					return children[i+1];
				} else {
					return NULL;
				}
			}
		}
	}
	return NULL;
}

#pragma mark -
#pragma mark Events

- (void)vdEventFired:(vdEventReceiver *)sender {
	if((!(flags & SHOW)) && (flags & AFFECTCHILDREN))
		return;
	
	vdNode **nodes = (vdNode **)malloc(count*sizeof(vdNode *));
	for(int i=0; i<count; i++)
	{
		nodes[i] = children[i];
	}
	
	volatile int tCount = count;
	
	if(sender.lastEvent->type == vdInputEvent)
	{
		vdInputMessage *message = (vdInputMessage *)sender.lastEvent->data;
		
		if(message->event == vdTouchesBegan)
		{
			for(int i=0; i<tCount; i++)
			{
				if([nodes[i] conformsToProtocol:@protocol(vdGUIProtocol)])
				{
					id temp = nodes[i];
					if([temp respondsToSelector:@selector(getTouch:)])
						[temp getTouch:message->touch[0]];
				} 
			}
		} 
		else if(message->event == vdTouchesMoved)
		{
			for(int i=0; i<tCount; i++)
			{
				if([nodes[i] conformsToProtocol:@protocol(vdGUIProtocol)])
				{
					id temp = nodes[i];
					if([temp respondsToSelector:@selector(getTouch:)])
						[temp getTouch:message->touch[0]];
				} 
			}
		}
		else if(message->event == vdTouchesEnded)
		{
			for(int i=0; i<tCount; i++)
			{
				if([nodes[i] conformsToProtocol:@protocol(vdGUIProtocol)])
				{
					id temp = nodes[i];
					if([temp respondsToSelector:@selector(endSequence:)])
						[temp endSequence:message->touch[0]];
				} 
			}
		}
		else if(message->event == vdTouchesCancelled)
		{
			for(int i=0; i<tCount; i++)
			{
				if([nodes[i] conformsToProtocol:@protocol(vdGUIProtocol)])
				{
					id temp = nodes[i];
					if([temp respondsToSelector:@selector(endSequence:)])
						[temp endSequence:message->touch[0]];
				}
			}
		}
		
		free(nodes);
	}
}

#pragma mark -
#pragma mark Actions

- (void)addAction:(vdAction *)_action {
	if(!action)
	{
		actions = 1;
		action = (vdAction **)malloc(actions*sizeof(vdAction *));
		
		action[0] = _action;
		return;
	}
	
	for(int i=0; i<actions; i++)
	{
		if(_action == action[i])
			return;
	}
	
	actions += 1;
	action = (vdAction **)realloc(action, actions*sizeof(vdAction *));
	action[actions-1] = _action;
	_action->retainCount += 1;
}

- (void)removeAction:(vdAction *)_action {
	if(!action)
		return;
	
	for(int i=0; i<actions; i++)
	{
		if(action[i] == _action)
		{
			vdDeleteAction(_action);
			for(int j=i; j<actions-1; j++)
			{
				action[j] = action[j+1];
			}
			
			actions -= 1;
			action = (vdAction **)realloc(action, actions*sizeof(vdAction *));
			return;
		}
	}
}

#pragma mark -
#pragma mark Drawing

- (void)setRenderTarget:(vdRenderTarget *)target {
	renderTarget = target;
	
	if(!(flags & AFFECTCHILDREN))
		return;
	
	for(int i=0; i<count; i++)
	{
		children[i].renderTarget = target;
	}
}

- (void)draw:(NSTimeInterval)deltaTime {
	// Overwrite this!
}

- (void)contact:(CGPoint)p :(NSTimeInterval)deltaTime {
	aPos_x = p.x + pos_x;
	aPos_y = p.y + pos_y;
	
	if(flags & SHOW)
	{
		setAtctiveRenderTarget(renderTarget);
		
		if(flags & BLEND)
		{
			if(flags & BRIGHT)
			{
				[[vdRenderer sharedRenderer] setBlendMode:YES sFactor:GL_SRC_ALPHA dFactor:GL_DST_ALPHA];
			} else {
				[[vdRenderer sharedRenderer] setBlendMode:YES sFactor:sourceBlendF dFactor:destBlendF];
			}
		} else {
			[[vdRenderer sharedRenderer] setBlendMode:NO sFactor:sourceBlendF dFactor:destBlendF];
		}
		
		[[vdRenderer sharedRenderer] setShader:nodeShader];
		
		if(actions && !(flags & IGNOREACTIONS))
		{
			for(int i=0; i<actions; i++)
			{
				currentAction = action[i];
				
				if(action[i]->objcClass)
				{
					[action[i]->target performSelector:action[i]->selector withObject:self];
				} else {
					action[i]->callback(self);
				}
			}
			
			currentAction = NULL;
		}
		
		[self draw:deltaTime];
	}
	
	if(!(flags & SHOW) && (flags & AFFECTCHILDREN))
		return;
	
	for(int i=0; i<count; i++)
	{
		[children[i] contact:CGPointMake(aPos_x, aPos_y) :deltaTime];
	}
}

#pragma mark -
#pragma mark Childs

- (void)cleanChildren {
	for(int i=0; i<count; i++)
	{
		[children[i] cleanChildren];
		
		int j = [children[i] retainCount];
		
		for(int k=0; k<j; k++)
		{
			[children[i] release];
		}
	}
	
	free(children);
	count = 0;
	children = (vdNode **)malloc(sizeof(vdNode *));
}

- (void)addReceiver {
	uiChilds += 1;
	
	if(!receiver)
	{
		receiver = [vdEventReceiver receiverWithDelegate:self andType:vdInputEvent];
		[[vdEventHandler sharedEventHandler] addReceiver:receiver];
	}
}

- (void)removeReceiver {
	uiChilds -= 1;
	
	if(uiChilds == 0 && receiver)
	{
		[[vdEventHandler sharedEventHandler] removeReceiver:receiver];
		[receiver release];
		receiver = NULL;
	}
}

- (void)addChild:(vdNode *)aNode withLayer:(int)aLayer {
	for(int i=0; i<count; i++)
	{
		if(children[i].layer > aLayer)
		{
			// Einsetzen
			count += 1;
			children = (vdNode **)realloc(children, count*sizeof(vdNode *));
			
			for(int j=count-1; j>i; j--)
			{
				children[j] = children[j-1];
			}
			
			children[i] = aNode;
			children[i].layer = aLayer;
			children[i].mother = mother;
			children[i].parent = self;
			children[i].father = father;
			children[i].renderTarget = renderTarget;
			
			if([children[i] conformsToProtocol:@protocol(vdGUIProtocol)])
			{
				[self addReceiver];
			}
			
			return;
		}
	}
	
	count += 1;
	int i = count-1;
	children = (vdNode **)realloc(children, count*sizeof(vdNode *));
	children[i] = aNode;
	children[i].layer = aLayer;
	children[i].mother = mother;
	children[i].parent = self;
	children[i].father = father;
	children[i].renderTarget = renderTarget;
	
	if([children[i] conformsToProtocol:@protocol(vdGUIProtocol)])
	{
		[self addReceiver];
	}
}

- (void)addChild:(vdNode *)aNode {
	[self addChild:aNode withLayer:0];
}

- (void)removeChild:(vdNode *)aNode cleanUp:(BOOL)clean {
	for(int i=0; i<count; i++)
	{
		if(children[i] == aNode)
		{
			if([children[i] conformsToProtocol:@protocol(vdGUIProtocol)])
			{
				[self removeReceiver];
			}
			
			if(clean)
			{
				[children[i] cleanChildren];
				
				int k = [children[i] retainCount];
				
				for(int j=0; j<k; j++)
					[children[i] release];
			}
			
			for(int j=i; j<count-1; j++)
			{
				children[j] = children[j+1];
			}
			
			count -= 1;
			children = (vdNode **)realloc(children, count*sizeof(vdNode *));
			
			return;
		}
	}
}

- (void)reorderChild:(vdNode *)aChild toLayer:(int)aLayer {
	for(int i=0; i<count; i++)
	{
		if(aChild == children[i])
		{
			[self removeChild:aChild cleanUp:NO];
			[self addChild:aChild withLayer:aLayer];
			
			return;
		}
	}
}

- (void)destroy {
	if(parent)
	{
		[parent removeChild:self cleanUp:YES];
	} else {
		[self cleanChildren];
		
		int i = [self retainCount];
		for(int j=0; j<i; j++)
			[self release];
	}
}

#pragma mark -
#pragma mark Con- Destructor

+ (vdNode *)node {
	return [[[vdNode alloc] init] autorelease];
}

- (id)init {
	if(self = [super init])
	{
		receiver = NULL;
		uiChilds = 0;
		
		count = 0;
		children = (vdNode **)malloc(sizeof(vdNode *));
		
		layer = 0;
		
		pos_x = 0;
		pos_y = 0;
		
		aPos_x = 0;
		aPos_y = 0;
		
		size_x = 0;
		size_y = 0;
		
		rotation = 0;
		
		scale_x = 1.0f;
		scale_y = 1.0f;
		
		flags = SHOW | BLEND | AFFECTCHILDREN;
		
		color = (vdColor *)malloc(sizeof(vdColor));
		
		color->alpha = 1.0f;
		color->red = 1.0f;
		color->green = 1.0f;
		color->blue = 1.0f;
		
		father = NULL;
		parent = NULL;
		
		sourceBlendF = GL_SRC_ALPHA;
		destBlendF = GL_ONE_MINUS_SRC_ALPHA;
		
		texture = NULL;
		nodeShader = NULL;
		renderTarget = NULL;
		
		currentAction = NULL;
		action = NULL;
		actions = 0;
		
		modelMatrix = NULL;
		
		vdLog(@"Created node %@", self);
	}
	return self;
}

- (void)dealloc {
	vdLog(@"Deleted node %@", self);
	
	free(color);
	free(children);
	
	if(action)
	{
		for(int i=0; i<actions; i++)
		{
			vdDeleteAction(action[i]);
		}
		free(action);
	}
	
	if(receiver)
	{
		[[vdEventHandler sharedEventHandler] removeReceiver:receiver];
		[receiver release];
	}
	
	[super dealloc];
}

@end
